package com.bookmarket.controller;

/**
 * Created by crossoverJie on 2016/8/7.
 */

import com.bookmarket.dao.LQContentMapper;
import com.bookmarket.entity.Content;
import com.bookmarket.service.LQContentService;
import com.bookmarket.websocket.GetHttpSessionConfigurator;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

//该注解用来指定一个URI，客户端可以通过这个URI来连接到WebSocket。

/**
 * 类似Servlet的注解mapping。无需在web.xml中配置。
 * configurator = SpringConfigurator.class是为了使该类可以通过Spring注入。
 */
//@ServerEndpoint(value = "/websocket/{user}", configurator = SpringConfigurator.class)
@Controller
@RequestMapping("/web")
@ServerEndpoint(value="/websocket/{user}",configurator=GetHttpSessionConfigurator.class)
public class LQ_MyWebSocket {
    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    public LQ_MyWebSocket() {

    }

//    @Resource 注入失败！原因：Configurator使用的是HttpSession，而非SpringConfigurator
//    private LQContentService contentService;

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    // 若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
//    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();

    private static Map<String,LQ_MyWebSocket> webSocketSet = new HashMap<String, LQ_MyWebSocket>();

    //与客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    private HttpSession httpSession;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     *                @PathParam("user")  用来得到建立连接申请中的属性
     */
    @OnOpen
    public void onOpen(Session session,EndpointConfig config,@PathParam("user") String key) throws Exception{
        this.session = session;
        //webSocketSet.add(this);     //加入set中
        this.httpSession= (HttpSession) config.getUserProperties().get(HttpSession.class.getName());//httpSession用来判断是否是同一个人

        System.out.println(this.session.getId());
        if(webSocketSet.containsKey(key)){//如果集合中存在该用户的编号
            if(webSocketSet.get(key).httpSession.equals(this.httpSession)){//且是同一个会话
                System.out.println("同一个人");
                webSocketSet.get(key).session = this.session;//刷新session
            }else{
                webSocketSet.get(key).session.getBasicRemote().sendText("KickOut");//发送消息
                webSocketSet.put(key,this); //加入Map中
                System.out.println("同人不同登录");
            }
        }else{
            webSocketSet.put(key,this); //加入Map中
            addOnlineCount();           //在线数加1
            System.out.println("不同的人");
        }

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);
//        处理过后的Map集合
        Map<String,String> map = mapUtils(message);
        //群发消息
//        for (MyWebSocket item : webSocketSet) {
//            try {
//                item.sendMessage(message);
//            } catch (IOException e) {
//                e.printStackTrace();
//                continue;
//            }
//        }
        //单发消息
        for(String key : webSocketSet.keySet()){//遍历集合中的key
            if(map.get("toName").equals(key)||map.get("fromName").equals(key)){//如果目标客户存在
                try{
                    webSocketSet.get(key).sendMessage(map);//向该客户发送信息
                    System.out.println("发送成功！源："+map.get("fromName")+"；目标："+map.get("toName"));
                }catch(IOException e) {
                    System.out.println("异常");
                    break;
                }
            }
        }
        //保存入数据库
        try{
            saveMessage(contentUtils(map));
        }catch(IOException e){
            e.printStackTrace();
        }

    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解，是根据自己需要添加的方法。
     */
    public void sendMessage(Map<String,String> map) throws IOException {

        Content content = contentUtils(map);

        Gson gson = new Gson();
        String json = gson.toJson(content);

        //this.session.getBasicRemote().sendText(message);
        this.session.getAsyncRemote().sendText(json);
    }

    public void saveMessage(Content content){
        //将聊天信息存入数据库
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-mybatis.xml");
        LQContentService contentService = ctx.getBean("contentService", LQContentService.class);//手动注入
        contentService.insertSelective(content);
    }

    /**
     * 查询在线的用户
     */
    @RequestMapping("/queryOnline")
    public ModelAndView queryOnline(){
        ModelAndView mv = new ModelAndView();
//        mv.setView(new MappingJackson2JsonView());
        mv.setViewName("backstage/HJHRoles");
        String str = "";
        for(String s:webSocketSet.keySet()){
            str += s+",";
        }
        mv.addObject("online",str);
        return mv;
    }

    /**
     * 剔除下线
     */
    @RequestMapping("/quit")
    public ModelAndView quit(String name) throws Exception{
        ModelAndView mv = new ModelAndView();
        mv.setView(new MappingJackson2JsonView());
        for(String key:webSocketSet.keySet()){
            if(key.equals(name)){
                webSocketSet.get(key).session.getBasicRemote().sendText("Quit");//发送消息
                webSocketSet.remove(key);
            }
        }
        mv.addObject("result","对方已下线!");
        return mv;
    }

    //分离Map集合的工具类
    public Map<String,String> mapUtils(String message){
        //对客户端发来的消息进行处理
        HashMap<String, String> map = new HashMap<String, String>();
        String msgString = message.toString();
        String m[] = msgString.split(",");
        map.put("fromID", m[0]);      //源ID
        map.put("fromName",m[1]); //源 姓名
        map.put("toID", m[2]);           //目标ID
        map.put("toName", m[3]);      //目标姓名
        map.put("content", m[4]);       //内容
        return map;
    }

    //将分离的Map组合成Content
    public Content contentUtils(Map<String,String> map) throws IOException{
        Content content = new Content();
        content.setAdminId(Integer.parseInt(map.get("fromID")));//发送人
        content.setAdminName(map.get("fromName"));//姓名
        content.setCustomId(Integer.parseInt(map.get("toID")));//接收人
        content.setCustomName(map.get("toName"));//姓名
        content.setContent(map.get("content"));//内容
        SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd HH:mm:dd");
        content.setCreateDate(sm.format(new Date()));//发送日期
        return content;
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        LQ_MyWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        LQ_MyWebSocket.onlineCount--;
    }

}
